# -*- coding: utf-8 -*-

"""
@author: Laowang
@contact: QQ:1125564921
@Created on: 2022/6/27
@Remark: 
"""

import time
import os
import threading
from fnmatch import fnmatch
from inspect import isclass
from typing import Dict, NoReturn

from .constant import *
from framework_strategy.gopy.base.type import Self
from framework_strategy.gopy.base.singleton import SingleMeta

class StrategyModule:
    """ """

    def __init__(self, dir: str) -> None:
        self._dir = dir

    def load_strategy_class_from_module(self):
        """ load module """

        module = __import__(self._dir, globals(), locals(), ["*"])
        return module


# strategy load
class StrategyLoad:

    """
    Strategy is loaded
    ::param : Strategy directory
    """

    def __init__(self, strategy_register: "StrategyRegister", app_dir: str = APP_DIR, queue=None) -> None:

        self._app_dir = app_dir
        self._active = False
        self.__thread = threading.Thread(target=self.loop_load, daemon=True, name="StrategyLoad")
        self.strategy_register = strategy_register
        self._queue = queue

    def load_strategy_from_folder(self):
        """load from folder"""

        dirs = os.listdir(self._app_dir)
        for d in dirs:
            stdirs = os.path.join(self._app_dir, d)
            if not os.path.isdir(stdirs):
                continue
            r_file = APP_ENGINE + "*"
            res_file_module = ".".join([APP_PACKAGE, d, APP_ENGINE])
            yield res_file_module if len(list(filter(lambda x: fnmatch(x, r_file), os.listdir(stdirs)))) > 0 else None

    def load_strategy_class_from_folder(self):
        """ load class """

        for d in self.load_strategy_from_folder():
            if not d:
                continue

            sm = StrategyModule(d)
            try:
                yield sm.load_strategy_class_from_module()
            except:
                pass

    def load_strategy_class_from_module(self):
        from framework_strategy.gopy.trader.engine import StrategyTemplate

        for module in self.load_strategy_class_from_folder():
            for __module in dir(module):

                value = getattr(module, __module)
                if isclass(value) and issubclass(value, StrategyTemplate) and value is not StrategyTemplate:
                    yield value

    def loop_load(self):

        while self._active:
            modules = dict()
            for module in self.load_strategy_class_from_module():
                with self.strategy_register.lock:
                    module_name = getattr(
                        module, "APP_NAME") or module.__name__
                    modules[module_name] = module
                    self.strategy_register.add_strategy_class(
                        module_name, module)
            self._queue.put(modules)
            time.sleep(2)

        print("[StrategyLoad]  exit ...")
            
    @property
    def isload(self):
        return self._isload

    def stop(self):
        self._active = False
        self.__thread.join()

    def start(self):
        self._active = True
        self.__thread.start()


# strategy reigster
# TODO 是否考虑记录对应的策略信息到缓存里
class StrategyRegister(SingleMeta):
    """"""

    def __init__(self) -> None:

        self._classes = {}
        self.lock = threading.Lock()

    def from_cls_init(self, main_engine, event_engine) -> Self:
        """"""
        setattr(self, "main_engine", main_engine)
        setattr(self, "event_engine", event_engine)
        return self

    def add_strategy_class(self, strategy_name: str, cls: object) -> NoReturn:
        """"""
        self._classes[strategy_name] = cls(self.main_engine, self.event_engine)

    def remove_strategy_class(self, strategy_name: str) -> NoReturn:
        """"""
        if strategy_name in self._classes:
            self._classes.pop(strategy_name)

    def get_strategy_all_class(self) -> Dict[str, object]:
        """"""
        return self._classes

    def get_strategy_class(self, strategy_name: str) -> object:
        """"""
        return self._classes.get(strategy_name)

    @property
    def classes(self):
        return self._classes
